Angular + Amplify DataStoreを試してみる #reinvent
どうも!大阪オフィスの西村祐二です。
この記事はAngular Advent Calendar 2019の16日目の記事です。
re:Invent 2019期間中に、Amplifyの新機能「Amplify DataStore」が発表されました!今回は、この機能をAngularで試してみたいと思います。
「Amplify DataStore」を使うことによってデバイスやブラウザのストレージを使ったオフライン対応などが簡単にできたり、開発者はローカルのデータを扱うような感覚で、簡単に分散されたデータを扱うことができます。
速報記事は下記になります。Amplify DataStoreの説明は下記を参照ください。
https://aws-amplify.github.io/docs/js/datastore
試してみる
環境
Angular CLI: 8.3.20 Node: 12.13.0 OS: darwin x64 Angular: 8.2.14
準備
CLIなどのツールをインストールしておきます。
$ npm i -g @angular/cli $ npm i -g @aws-amplify/cli@latest
Angularで雛形のサンプルアプリケーションを作ります。
$ ng new datastore-demo --style=scss --routing $ cd datastore-demo
amplifyの設定など追加する初期セットアップをします。
$ npx amplify-app@latest
必要なライブラリをインストールしておきます。
$ npm i @aws-amplify/core @aws-amplify/datastore
GraphQLスキーマを変更
今回のサンプルでは下記のスキーマを使用します。
enum PostStatus { ACTIVE INACTIVE } type Post @model { id: ID! title: String! rating: Int! status: PostStatus! }
ファイルの編集が終わったら下記コマンドを実行します。
$ npm run amplify-modelgen
コマンドの実行が完了するとCFnであったり、GraphQLスキーマであったり、いろいろなファイルを生成してくれます。
AWSリソースをデプロイ
下記コマンドを実行してAWSリソースをデプロイします。
$ npm run amplify-push
npm run amplify-push
でエラーが出る場合amplify init
を実行してから再度実行すると解消するかもしれません。
デプロイが完了すると
- DynamoDB
- AppSync
などのリソースが作成されます。
▼AppSyncに作成されたAPI
▼DynamoDBに作成されたテーブル
src/aws-exports.js
にappsyncなどのエンドポイントや認証情報が記載されたファイルが生成されます。Githubに載せるときは取り扱いにご注意ください。
Angularであれば、environments
のファイルに他の情報と一緒に記載し、管理するほうが行儀がいいかもしれません。
初期設定を行う
AngularでAmplifyを利用する際、はじめに設定を変更しておきます。
"compilerOptions": { "types" : ["node"] }
(window as any).global = window; (window as any).process = { env: { DEBUG: undefined }, }; global.Buffer = global.Buffer || require('buffer').Buffer;
実装していく
ロジック
下記のように実装しました。
動作確認が目的のため、必要最低限の機能のみ実装しています。また、サンプル実装なので実装内容についてはご了承ください。
一部、エラーが解決できず、anyを利用しています。
import { Component, OnInit } from '@angular/core'; import Amplify from '@aws-amplify/core'; import { DataStore, Predicates } from '@aws-amplify/datastore'; import { Post, PostStatus } from '../models'; import { environment } from '../environments/environment'; import { from } from 'rxjs'; @Component({ selector: 'app-root', templateUrl: './app.component.html', styleUrls: ['./app.component.scss'] }) export class AppComponent implements OnInit { title = 'datastore-demo'; data = from(DataStore.query(Post, Predicates.ALL)); constructor() { Amplify.configure(environment.amplify.AppSync); } ngOnInit() { this.subscription(); } create() { DataStore.save( new Post({ title: `New title ${Date.now()}`, rating: 1, status: PostStatus.ACTIVE }) ); } deleteAll() { DataStore.delete(Post, Predicates.ALL); } list() { this.data = from(DataStore.query(Post, Predicates.ALL)); } delete(id: string) { this.fetch(id).subscribe(item => DataStore.delete(item)); } fetch(id: string) { return from(DataStore.query(Post as any, id)); } update(id: string) { this.fetch(id).subscribe(item => { DataStore.save( Post.copyOf(item as any, updated => { updated.title = `title ${Date.now()}`; updated.rating = 4; }) ); }); } subscription() { DataStore.observe(Post as any).subscribe(msg => { this.list(); console.log(msg); }); } }
特徴的なところを簡単に説明します。
- 6行目:
src/aws-exports.js
に記載された情報はenvironments
のファイルに移動させています。 - 16行目:Async Pipeを使いたいので
from
で囲ってobservableにしています。 - 21行目:データの変更があったら画面を更新するために、subscriptionで購読するようにしています。
- 23行目:
DataStore
を使ってitemを登録する処理です。idは自動的に付与されます。 - 44行目:itemを更新する処理です。titleとratingを更新します。versionは自動的に更新されます。
画面
動作確認が目的のため、必要最低限の機能のみ実装します。また、サンプル実装なので実装内容についてはご了承ください。
- 表にはDynamoDBに登録されているデータが表示されます。
- 「NEW」ボタンで新しい
item
を登録します。 - 「DELETE ALL」ボタンですべてのデータを削除します。
delete item
にid
を入力してボタンをクリックすると入力したitem
を削除します。update item
にid
を入力してボタンをクリックするとtitle
とrating
とversion
が更新されます。
<h1> {{ title }} </h1> <table border="1"> <tr> <th>id</th> <th>title</th> <th>rating</th> <th>_version</th> </tr> <tr *ngFor="let item of data | async"> <td>{{ item.id }}</td> <td>{{ item.title }}</td> <td>{{ item.rating }}</td> <td>{{ item._version }}</td> </tr> </table> <div> <input type="button" value="NEW" (click)="create()" /> <input type="button" value="DELETE ALL" (click)="deleteAll()" /> </div> <div> delete item:<input #id /> <button (click)="delete(id.value); id.value = ''">delete</button> </div> <div> update item:<input #id /> <button (click)="update(id.value); id.value = ''">update</button> </div>
動作確認
下記コマンドでローカル実行し、http://localhost:4200
にアクセスします。
$ ng serve
chromeのdevtoolで確認するとIndexedDB
にdatabaseが作成されていることがわかります。
- 追加
画面のNEWボタンをクリックします。
すると、DynamoDBには下記のようなデータが追加されます。作成更新時刻、バージョン情報など付随情報が自動的に追加されています。
- 更新
idを入力して更新してみます。購読しているので、他のブラウザで開いた状態でも、自動的に更新されます。
- 削除
削除を実行すると、DynamoDBのデータ自体は削除されず、バージョンのインクリメントと、deleteフラグの付与とTTLの設定がされる挙動となっていました。
オフラインモードを試す
Datastore
を使っていれば、オフラインモード用にコードを追加・変更は不要です。
下記Gifのように、ネットワークを切った状態でも問題なく、データの追加・更新ができました。
また、画面リロードしてもデータが取得されました。
ネットワークを復活させると、サーバと通信がはじまり、バージョン情報の付与など実行され同期されたことがわかります。
さいごに
Angular + Amplify DataStoreを試してみました。
かなり強力な機能でした。
AmplifyとAWS AppSyncと組み合わせるとことで、オフライン対応したアプリケーションを簡単に構築することができました。
オフライン時の挙動を開発者側で実装せずに済むのでかなりの工数削減が期待できそうです。
ただ、DynamoDBのデータを削除してもアプリケーションでは表示され続けるなどといったことがあったので、挙動はきちんと理解しておく必要があるかと思います。
上記はブラウザのIndexedDB
に保存されるデータベースを削除することで解消しました。
興味のある方は是非試してみてください。
誰かの参考になれば幸いです。